#include "FreeRTOS.h"
#include "task.h"

#include "app_at.h"
#include "app_task.h"
#include "app_bt.h"
#include "app_ble.h"
#include "app_btdm.h"
#include "app_config.h"

#include "app_lvgl.h"
// #include "fr_device_charge.h"
#include "fr_device_pmu_io.h"
#include "fr_device_button.h"
// #include "fr_device_pa.h"
#include "fr_device_encode.h"
// #include "fr_device_vbat.h"

static struct co_list event_list;

extern TaskHandle_t app_task_handle;

void hci_controller_read(void);
void hci_host_read(void);

static void app_task_event_handler(void)
{
    struct app_task_event *event = NULL;

    vTaskSuspendAll();
    taskENTER_CRITICAL();
    event = (struct app_task_event *)co_list_pop_front(&event_list);
    taskEXIT_CRITICAL();
    xTaskResumeAll();


    if(event) {
        switch(event->event_type) {
            case APP_TASK_EVENT_AT_CMD:
                app_at_cmd_recv_handler(event->param, event->param_len);
                break;
            case APP_TASK_EVENT_HOST_INITED:
                app_lvgl_init();
                app_btdm_start();
                break;
            case APP_TASK_EVENT_PMU_WAKEUP:
                printf("this is pmu_io0 wakeup\r\n");
                break;
            case APP_TASK_EVENT_GPIO_PD_WAKEUP:
                printf("this is gpio_PD wakeup\r\n");
                break;

            case APP_TASK_EVENT_BTN_TOGGLE:
                {
                    button_toggle_handler(*(uint32_t *)event->param);
                }
                break;

            case APP_TASK_EVENT_BTN_OUTPUT:
                {
                    button_event_handler(event->param);
                }
                break;
            
            case APP_TASK_EVENT_ENCODE_TOGGLE:
                {
                    encode_event_handle(*(uint32_t *)event->param);
                }
                break;

            case APP_TASK_EVENT_WAKEUP_GUITASK:
                {
                    gui_task_resume();
                }   
                break;  

            default:
                break;
        }

        vPortFree((void *)event);
    }
}

void app_task_event_post(struct app_task_event *event, bool high)
{
    uint32_t old_basepri;
    
    if(xPortIsInsideInterrupt()) {
        old_basepri = taskENTER_CRITICAL_FROM_ISR();
        if(high) {
            co_list_push_front(&event_list, &event->hdr);
        }
        else {
            co_list_push_back(&event_list, &event->hdr);
        }
        taskEXIT_CRITICAL_FROM_ISR(old_basepri);
        vTaskNotifyGiveFromISR(app_task_handle, NULL);
    }
    else {
        taskENTER_CRITICAL();
        if(high) {
            co_list_push_front(&event_list, &event->hdr);
        }
        else {
            co_list_push_back(&event_list, &event->hdr);
        }
        taskEXIT_CRITICAL();
        xTaskNotifyGive(app_task_handle);
    }
}

struct app_task_event *app_task_event_alloc(uint8_t event_type, uint32_t param_len, bool block)
{
    struct app_task_event *event = NULL;
    
    while(event == NULL) {
        event = pvPortMalloc(sizeof(struct app_task_event) + param_len);
        if (event == NULL) {
            if (block) {
                vTaskDelay(10);
            }
            else {
                return NULL;
            }
        }
    }
    
    event->event_type = event_type;
    event->param_len = param_len;
    
    return event;
}

static void app_task(void *arg)
{
    co_list_init(&event_list);
    
    app_btdm_init();  

    device_pmu_io_init();

#if BOARD_SEL == BOARD_EVB_FR5090
    button_init(BUTTON_PIN_NUM|SOS_KEY_PIN_NUM|KEY2_PIN_NUM|KEY1_PIN_NUM);
#elif BOARD_SEL == BOARD_EVB_FR3092E_RGB
    #if BOARD_VERSION == BOARD_V1_0
        button_init(BUTTON_PIN_NUM|SOS_KEY_PIN_NUM|KEY2_PIN_NUM|KEY1_PIN_NUM);
    #elif BOARD_VERSION == BOARD_V1_3
        button_init(BUTTON_PIN_NUM|KEY2_PIN_NUM|KEY1_PIN_NUM);
    #else
    #error "choose correct board"
    #endif
#else
#error "choose correct board"
#endif

    while(1) {
        ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
        app_task_event_handler();
    }
}

void app_task_init(void)
{
    xTaskCreate(app_task, "app", APP_TASK_STACK_SIZE, NULL, APP_TASK_PRIORITY, &app_task_handle);
}
